<?php

namespace core\base;

use core\exception\CommonException;
use core\lib\Handle;
use core\lib\LibUtil;
use core\lib\R;
use Yii;
use yii\helpers\ArrayHelper;

class BaseService
{

    /**
     * @var null
     */
    private $transaction = null;

    /**
     * @var BaseModel
     */
    protected $model = null;

    public function beginTransaction()
    {
        $this->transaction = \Yii::$app->db->beginTransaction();
    }

    /**
     * 判断是否开启事务
     * @throws \Exception
     */
    public function checkTransaction(){
        if ($this->transaction == null) {
            throw new \Exception('没有开启事物', 500);
        }
    }

    /**
     * 提交事务
     */
    public function commit()
    {
        $this->checkTransaction();
        $this->transaction->commit();
    }

    /**
     * 回滚事务
     */
    public function rollback()
    {
        $this->checkTransaction();
        $this->transaction->rollback();
    }

    public function getTableName($class = __CLASS__)
    {
        $class = end(explode('\\', $class));
        $tableName = LibUtil::humpToLine(substr($class, 0, strlen($class) - 7));
        return strtolower($tableName);
    }

    public function getScheme()
    {
        $tableName = $this->getTableName();
        return Yii::$app->db->schema->getTableSchema($tableName);
    }

    /**
     * 注 使用这些方法线上最好开启scheme缓存
     * @param $withMethod bool 是否返回获取对应数据的R函数类型
     * @return array
     */
    public function getField($withMethod = false)
    {
        $tableSchema = $this->getScheme();
        $fields = ArrayHelper::getColumn($tableSchema->columns, 'name', false);
        if ($withMethod) {
            $rs = [];
            $columns = $tableSchema->columns;
            foreach ($fields as $field) {
                $rs[$field] = $this->getRMethod($columns[$field]->type);
            }
            $fields = $rs;
        }
        return $fields;
    }

    public function getPk()
    {
        $pk = $this->getScheme()->primaryKey;
        return empty($pk) ? '' : $pk[0];
    }

    public function getRMethod($type)
    {
        $intType = ['integer', 'smallint', 'bigint', 'integer'];
        if (in_array($type, $intType)) {
            return 'numeric';
        }
        return 'not_safe';
    }

    public function save($checkEmpty = false)
    {
        $pk = $this->getPk();
        $fields = $this->getField(true);
        $args = [];
        // 获取所有的参数
        foreach ($fields as $field => $method) {
            $args[$field] = R::$method($field);
        }
        $pkValue = $args[$pk];
        if ($checkEmpty && LibUtil::arrayIsEmpty($args)) {
            throw new CommonException('数据全为空无法执行保存操作', 500);
        }
        // args中移除pk的值
        unset($args[$pk]);
        $m = null;
        $checkFlag = false;
        try {
            // 存在主键数据 则更新数据
            if (isset($pkValue) && !empty($pkValue)) {
                $m = $this->model->findOneByPk($pkValue);
                if (empty($m)) {
                    throw new CommonException('not exist record', 500);
                }
                // 判断数据是否有改变
                foreach ($args as $field => $value) {
                    if ($value != $m->getAttribute($field)) {
                        $checkFlag = true;
                    }
                }
            } else {
                $m = $this->model;
                $checkFlag = true;
            }
            $m->setAttributes($args);
            $rs = $m->save(false);
            if ($checkFlag && !$rs) {
                throw new CommonException('save fail...', 500);
            }
        } catch (\Exception $e) {
            return Handle::handleException($e, 'exception', 'save exception');
        }
        return [
            'code' => 200,
            'msg' => 'success',
            'data' => $m->$pk,
        ];
    }
}
